﻿/*
* 
* 
* Email: 
* Created Date: 
* Last Update: 
* @change:
*  - Add roundTo() function to Math object
*/

; (function ($) {
    $.isObject = function (v) {
        return !!v && Object.prototype.toString.call(v) === '[object Object]';
    };
    $.isDate = function (v) {
        return Object.prototype.toString.call(v) === '[object Date]';
    };
})(jQuery);


(function ($) {
    /**
    * Remove a css attribute like: 'border', 'font', etc. 
    * @param {Object} $p         A jQuery object of current element
    * @param {String} cssName    The name of css attribute
    */
    function removeCss($p, cssName) {
        if (!$p || !cssName || cssName.length <= 0)
            return;
        var originalStyle = $p.attr('style');
        if (originalStyle && originalStyle.length > 0) {
            var newStyle = $.grep(originalStyle.split(';'), function (css) {
                return css.toUpperCase().indexOf(cssName.toUpperCase()) < 0;
            }).join(';');

            $p.attr('style', newStyle);
        }
    };

    $.removeCss = function () {
        removeCss.apply($, arguments);
    };
    $.fn.extend({
        removeCss: function (cssName) {
            // Make it chainable
            return this.each(function () {
                removeCss.apply($, [$(this), cssName]);
            });
        }
    });
})(jQuery);


(function ($) {
    /**
    * Detect whether current device is touch devices
    */
    if ($.touchDevices === undefined) {
        var touchDevices = {
            _match: function (regex) {
                return navigator.userAgent.toLowerCase().match(regex);
            },
            isIpad: function () {
                return this._match(/iPad/i);
            },
            isIphone: function () {
                return this._match(/iPhone/i);
            },
            isIpod: function () {
                return this._match(/iPod/i);
            },
            isAndroid: function () {
                return this._match(/Android/i);
            },
            isBlackBerry: function () {
                return this._match(/BlackBerry/i);
            },
            isWindowsPhone: function () {
                return this._match(/Windows Phone/i);
            },
            isZuneWP7: function () {
                return this._match(/ZuneWP7/i);
            },
            isTouchDevice: function () {
                return this.isIpad()
                    || this.isIphone()
                    || this.isIpod()
                    || this.isAndroid()
                    || this.isBlackBerry()
                    || this.isWindowsPhone()
                    || this.isZuneWP7();
            }
        };

        $.touchDevices = $.extend({}, touchDevices);
    }

    /**
    * Since jQuery verion 1.9, the jQuery.browser is no longer available as its changelog
    */

    // Useragent RegExp
    var rwebkit = /(webkit)[ \/]([\w.]+)/,
	    ropera = /(opera)(?:.*version)?[ \/]([\w.]+)/,
	    rmsie = /(msie) ([\w.]+)/,
	    rmozilla = /(mozilla)(?:.*? rv:([\w.]+))?/;

    function uaMatch(ua) {
        ua = ua.toLowerCase();

        var match = rwebkit.exec(ua) ||
			ropera.exec(ua) ||
			rmsie.exec(ua) ||
			ua.indexOf("compatible") < 0 && rmozilla.exec(ua) ||
			[];

        return { browser: match[1] || "", version: match[2] || "0" };
    }

    if ($.browser === undefined) {
        $.browser = {
            webkit: false,
            opera: false,
            msie: false,
            mozilla: false
        };

        var browserMatch = uaMatch(navigator.userAgent);
        if (browserMatch.browser) {
            $.browser[browserMatch.browser] = true;
            $.browser.version = browserMatch.version;
        }

        // Deprecated, use jQuery.browser.webkit instead
        if (jQuery.browser.webkit) {
            jQuery.browser.safari = true;
        }
    }
})(jQuery);

/**
* A extension function for Math object to round a number with a specific number of decimal
* @param {Number} num           Number to round
* @param {Number} decimalNum    Number of decimal to round
*/
Math.roundTo = function (num, decimalNum) {
    if (typeof num === 'number') {
        if (typeof decimalNum !== 'number' || decimalNum === 0) {
            return Math.round(num);
        } else {
            decimalNum = Math.round(Math.abs(decimalNum));
            return Math.round(num * Math.pow(10, decimalNum)) / Math.pow(10, decimalNum);
        }
    }
    return num;
};

/**
* Extension functions for Array
*/
Array.prototype.copy = function () {
    return this.slice(0);
};
Array.prototype.min = function () {
    return Math.min.apply(Math, this);
};
Array.prototype.max = function () {
    return Math.max.apply(Math, this);
};
Array.prototype.sum = function () {
    var sum = 0;
    var length = this.length;
    for (var i = 0; i < length; i++)
        sum += this[i];
    return sum;
};
Array.prototype.indexOf = function (item, start) {
    if (typeof (item) === "undefined") return -1;
    var length = this.length,
        isFunc = typeof (item) === 'function';
    if (length !== 0) {
        start = start - 0;
        if (isNaN(start)) {
            start = 0;
        }
        else {
            if (isFinite(start)) {
                start = start - (start % 1);
            }
            if (start < 0) {
                start = Math.max(0, length + start);
            }
        }

        for (var i = start; i < length; i++) {
            if (typeof (this[i]) !== "undefined") {
                if (isFunc) {
                    var f = item.call(this[i], this[i]);
                    if ((typeof (f) !== 'undefined') && (f === true)) {
                        return i;
                    }
                }
                else {
                    if (this[i] === item)
                        return i;
                }
            }
        }
    }
    return -1;
};
Array.prototype.clear = function () {
    this.length = 0;
};
Array.prototype.addRange = function (rangeArr) {
    if (rangeArr && rangeArr.constructor.toString().indexOf('Array') != -1) {
        var copy = rangeArr.copy(),
            i = 0,
            len = copy.length;
        for (; i < len; i++)
            this.add(copy[i]);
        copy.clear();
    }
};
Array.prototype.contains = function (item) {
    return this.indexOf(item) >= 0;
};
Array.prototype.remove = function (item) {
    var index = this.indexOf(item);
    if (index >= 0) {
        this.splice(index, 1);
    }
    return (index >= 0);
};
Array.prototype.removeAt = function (index) {
    this.splice(index, 1);
}
Array.prototype.add = Array.prototype.push;

Array.prototype.insert = function (item, index) {
    this.splice(index, 0, item);
};

Array.prototype.each = function (func) {
    if (!$.isFunction(func))
        return;
    for (var i = 0, len = this.length; i < len; i++) {
        //func(i, this[i]);
        if (func.call(this[i], i, this[i]) === false) {
            break;
        }
    }
};

//Array.prototype.forEach = Array.prototype.each;


/**/
/**
* Gets the number of days in the month, given a year and month value. Automatically corrects for LeapYear.
* @param {Number}   The year (0-9999).
* @param {Number}   The month (0-11).
* @return {Number}  The number of days in the month.
*/
Date.getDaysInMonth = function (year, month) {
    return [31, (Date.isLeapYear(year) ? 29 : 28), 31, 30, 31, 30, 31, 31, 30, 31, 30, 31][month];
};
Date.prototype.getDaysInMonth = function () {
    return Date.getDaysInMonth(this.getFullYear(), this.getMonth());
};
Date.prototype.addMilliseconds = function (value) {
    this.setMilliseconds(this.getMilliseconds() + value);
    return this;
};
/**
* Determines if the current date instance is within a LeapYear.
* @param {Number}   The year (0-9999).
* @return {Boolean} true if date is within a LeapYear, otherwise false.
*/
Date.isLeapYear = function (year) {
    return (((year % 4 === 0) && (year % 100 !== 0)) || (year % 400 === 0));
};
Date.prototype.addSeconds = function (second) {
    this.addMilliseconds(second * 1000);
    return this;
};
Date.prototype.addMinutes = function (minute) {
    this.addMilliseconds(minute * 60000); /* 60*1000 */
    return this;
};
Date.prototype.addHours = function (hour) {
    this.addMilliseconds(hour * 3600000); /* 60*60*1000 */
    return this;
};
Date.prototype.addDay = function (day) {
    this.addMilliseconds(day * 86400000); /* 60*60*24*1000 */
    return this;
};
Date.prototype.addYear = function (year) {
    this.addMonth(year * 12);
    return this;
};
Date.prototype.addMonth = function (month) {
    var n = this.getDate();
    this.setDate(1);
    this.setMonth(this.getMonth() + month);
    this.setDate(Math.min(n, this.getDaysInMonth()));
    return this;
};
Date.prototype.addWeeks = function (week) {
    return this.addMilliseconds(week * 604800000); /* 60*60*24*7*1000 */
};

Date.prototype.toJSON = function () {
    var month = this.getMonth() + 1;
    if (month < 10)
        month = '0' + month;
    var day = this.getDate();
    if (day < 10)
        day = '0' + day;
    var year = this.getFullYear();
    var hours = this.getHours();

    if (hours < 10)
        hours = '0' + hours;
    var minutes = this.getMinutes();
    if (minutes < 10)
        minutes = '0' + minutes;

    var seconds = this.getSeconds();
    if (seconds < 10)
        seconds = '0' + seconds;
    var milli = this.getMilliseconds();
    if (milli < 100)
        milli = '0' + milli;
    if (milli < 10)
        milli = '0' + milli;
    return year + '-' + month + '-' + day + 'T' + hours + ':' + minutes + ':' + seconds + '.' + milli;
};

// 
// Utility for quickly convert time from client's timezone to page's timezone
Date.prototype.toPageTimezone = function () {
    return Eu2.Util.toPageTimezone(this);
};

Date.prototype.toUTC = function () {
    this.addMinutes((0 - Eu2.Util.getClientTimezone()) * 60)
    return this;
};

/*
* Checks dates equality (ignores time) 
*/
Date.prototype.equalsTo = function (date) {
    return ((this.getFullYear() == date.getFullYear()) &&
		(this.getMonth() == date.getMonth()) &&
		(this.getDate() == date.getDate()) &&
		(this.getHours() == date.getHours()) &&
		(this.getMinutes() == date.getMinutes()));
};

//
// String's extension function
String.prototype.leftPad = function (l, c) { return new Array(l - this.length + 1).join(c || '0') + this; }

String.prototype.format = function () {
    arguments = arguments || [];
    var args = [];
    for (var i = 0, len = arguments.length; i < len; i++)
        args[i] = arguments[i];
    return this.replace(/\{(\d+)\}/g, function (m, i) {
        return args[i];
    });
};

String.prototype.toBase64 = function () {
    var _str = this;
    return StringBase64.encode(_str);
};

String.prototype.fromBase64 = function () {
    var _base64Str = this;
    return StringBase64.decode(_base64Str);
};
/**
*
*  StringBase64 encode / decode
*  http://www.webtoolkit.info/
*
**/

var StringBase64 = {

    // private property
    _keyStr: "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",

    // public method for encoding
    encode: function (input) {
        var output = "";
        var chr1, chr2, chr3, enc1, enc2, enc3, enc4;
        var i = 0;

        input = StringBase64._utf8_encode(input);

        while (i < input.length) {

            chr1 = input.charCodeAt(i++);
            chr2 = input.charCodeAt(i++);
            chr3 = input.charCodeAt(i++);

            enc1 = chr1 >> 2;
            enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
            enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
            enc4 = chr3 & 63;

            if (isNaN(chr2)) {
                enc3 = enc4 = 64;
            } else if (isNaN(chr3)) {
                enc4 = 64;
            }

            output = output +
			this._keyStr.charAt(enc1) + this._keyStr.charAt(enc2) +
			this._keyStr.charAt(enc3) + this._keyStr.charAt(enc4);

        }

        return output;
    },

    // public method for decoding
    decode: function (input) {
        var output = "";
        var chr1, chr2, chr3;
        var enc1, enc2, enc3, enc4;
        var i = 0;

        input = input.replace(/[^A-Za-z0-9\+\/\=]/g, "");

        while (i < input.length) {

            enc1 = this._keyStr.indexOf(input.charAt(i++));
            enc2 = this._keyStr.indexOf(input.charAt(i++));
            enc3 = this._keyStr.indexOf(input.charAt(i++));
            enc4 = this._keyStr.indexOf(input.charAt(i++));

            chr1 = (enc1 << 2) | (enc2 >> 4);
            chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
            chr3 = ((enc3 & 3) << 6) | enc4;

            output = output + String.fromCharCode(chr1);

            if (enc3 != 64) {
                output = output + String.fromCharCode(chr2);
            }
            if (enc4 != 64) {
                output = output + String.fromCharCode(chr3);
            }

        }

        output = StringBase64._utf8_decode(output);

        return output;

    },

    // private method for UTF-8 encoding
    _utf8_encode: function (string) {
        string = string.replace(/\r\n/g, "\n");
        var utftext = "";

        for (var n = 0; n < string.length; n++) {

            var c = string.charCodeAt(n);

            if (c < 128) {
                utftext += String.fromCharCode(c);
            }
            else if ((c > 127) && (c < 2048)) {
                utftext += String.fromCharCode((c >> 6) | 192);
                utftext += String.fromCharCode((c & 63) | 128);
            }
            else {
                utftext += String.fromCharCode((c >> 12) | 224);
                utftext += String.fromCharCode(((c >> 6) & 63) | 128);
                utftext += String.fromCharCode((c & 63) | 128);
            }

        }

        return utftext;
    },

    // private method for UTF-8 decoding
    _utf8_decode: function (utftext) {
        var string = "";
        var i = 0;
        var c = c1 = c2 = 0;

        while (i < utftext.length) {

            c = utftext.charCodeAt(i);

            if (c < 128) {
                string += String.fromCharCode(c);
                i++;
            }
            else if ((c > 191) && (c < 224)) {
                c2 = utftext.charCodeAt(i + 1);
                string += String.fromCharCode(((c & 31) << 6) | (c2 & 63));
                i += 2;
            }
            else {
                c2 = utftext.charCodeAt(i + 1);
                c3 = utftext.charCodeAt(i + 2);
                string += String.fromCharCode(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63));
                i += 3;
            }
        }
        return string;
    }
};

/**
* Returns an HTML fragment of html template with the specified values applied.
* @html: a html string (i.e. <div>{foo}</div> or <div>{0}</div>)
* @values
* The template values. Can be an array if the params are numeric (i.e. {0})
* or an object (i.e. {foo: 'bar'}).
* @return {String} The HTML fragment
*/
String.applyTemplate = function (html, values) {
    var reg = /\{([\w-]+)\}/g;
    if (typeof html == 'undefined')
        return '';

    if ($.isArray(html))
        html = html.join('');

    if (typeof (html) === 'string' && ($.isArray(values) || $.isObject(values))) {
        return html.replace(reg, function (m, name) {
            return values[name] !== undefined ? values[name] : "";
        });
    }
    return html;
};

/**
* The pair of number between arabic-latin numbers
* @author: binh.nguyen@euroland.com
* @date: 18-June-2012
*/
String.arabic_latin_number = [
    ['0', '٠'],
    ['1', '١'], ['2', '٢'], ['3', '٣'],
    ['4', '٤'], ['5', '٥'], ['6', '٦'],
    ['7', '٧'], ['8', '٨'], ['9', '٩']
];

/**
* Convert a number (or string of number) to arabic digits
* @param {String, Number} value - The input value to convert
* @return {String}
* @author: binh.nguyen@euroland.com
* @date: 18-June-2012
*/
String.toArabicNumber = function (value) {
    var str = '' + value;
    var pair = String.arabic_latin_number.copy();
    for (var i = 0; i < pair.length; i++) {
        str = str.replace(new RegExp(pair[i][0], 'ig'), pair[i][1]);
    }

    return str;
};

/**
* Convert a number (or string of number) with arabic digits to latin digits
* @param {String, Number} value - The input value to convert
* @return {String}
* @author: binh.nguyen@euroland.com
* @date: 18-June-2012
*/
String.fromArabicNumber = function (value) {
    var str = '' + value;
    var pair = String.arabic_latin_number.copy();
    for (var i = 0; i < pair.length; i++) {
        str = str.replace(new RegExp(pair[i][1], 'ig'), pair[i][0]);
    }

    return str;
};

/**
* Replace all match characters match with new characters
* @param {String} oldString - Old string to be removed
* @param {String} newString - New string to replace with
* @return {String}
* @author: binh.nguyen@euroland.com
* @date: 18-June-2012
*/
String.prototype.replaceAll = function (oldString, newString) {
    newString = newString || '';
    return this.split(oldString).join(newString);
};

/**
* Convert a JSON Date string of Microsoft JavascriptSerializer to Date object
*/
String.prototype.toDate = function () {
    var usDate = null;
    var msDateString = this;
    var msDateRegex = new RegExp('\\/Date\\(([-+])?(\\d+)(?:[+-]\\d{4})?\\)\\/');
    if (msDateRegex.test(msDateString)) {
        var r = (msDateString || '').match(msDateRegex);
        usDate = r ? new Date(((r[1] || '') + r[2]) * 1) : null;
    }
    return usDate;
};